﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace MadLibrary.Net.Ndmp
{
    /// <summary>
    /// Serves as listener which is listening for new connections and which creates new services of specified type.
    /// </summary>
    /// <typeparam name="ServiceType">The type of the service which has to be created and which has to process connected client.</typeparam>
    public class ServiceListener<ServiceType> where ServiceType : Service, new()
    {
        /// <summary>
        /// Gets or sets the listening end point.
        /// </summary>
        /// <value>
        /// The listening end point.
        /// </value>
        public IPEndPoint ListeningEndPoint { get; set; }
        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="ServiceListener&lt;ServiceType&gt;"/> is running.
        /// </summary>
        /// <value>
        ///   <c>true</c> if running; otherwise, <c>false</c>.
        /// </value>
        public bool Running { get; set; }
        /// <summary>
        /// Gets or sets the listener.
        /// </summary>
        /// <value>
        /// The listener.
        /// </value>
        protected Socket listener { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceListener&lt;ServiceType&gt;"/> class.
        /// </summary>
        public ServiceListener()
        {
            this.ListeningEndPoint = new IPEndPoint(IPAddress.Any, 4600);
            this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceListener&lt;ServiceType&gt;"/> class with specified listening port.
        /// </summary>
        /// <param name="Port">The port.</param>
        public ServiceListener(int Port)
        {
            this.ListeningEndPoint = new IPEndPoint(IPAddress.Any, Port);
            this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceListener&lt;ServiceType&gt;"/> class with specified listening end point.
        /// </summary>
        /// <param name="ListeningEndPoint">The listening end point.</param>
        public ServiceListener(IPEndPoint ListeningEndPoint)
        {
            this.ListeningEndPoint = ListeningEndPoint;
            this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        /// <summary>
        /// Starts this instance's listening.
        /// </summary>
        public void Start()
        {
            this.Running = true;
            new Thread(new ThreadStart(()=>{
                this.listener.Bind(this.ListeningEndPoint);
                this.listener.Listen(100);
                while(this.Running)
                {
                    new Thread(new ThreadStart(()=>{
                        Socket client = this.listener.Accept();
                        ServiceType svc = new ServiceType();
                        if(this.Running)
                        {
                            int received = 0;
                            byte[] buffer = new byte[short.MaxValue];
                            while((received = client.Receive(buffer))<1 && this.Running)
                            {}
                            Request r = new Request();
                            (r as IParsable).Parse(Encoding.ASCII.GetString(buffer, 0, received));
                            Response resp = svc.ProcessClient(r);
                            client.Send(Encoding.ASCII.GetBytes(resp.ToString()));
                        }
                    })).Start();
                }
            })).Start();
        }
        /// <summary>
        /// Ends listening of this instance.
        /// </summary>
        public void End()
        {
            this.Running = false;
        }
        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="ServiceListener&lt;ServiceType&gt;"/> is reclaimed by garbage collection.
        /// </summary>
        ~ServiceListener()
        {
            this.Running = false;
        }
    }
}
